/*
	       File: ToolBox.cs
	Create Date: 2007-09-03
	    Version:

	Description:


	 Change log:


*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Configuration;
using System.Drawing;
using System.Drawing.Design;
using System.Data;
using System.IO;
using System.Reflection;
using System.Text;
using System.Windows.Forms;

using System.Workflow.Activities;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Design;
using System.Workflow.ComponentModel.Compiler;

using GuerillaProgrammer.FileProcessor;
using GuerillaProgrammer.FileProcessor.Settings;

namespace GuerillaProgrammer.FileProcessor
{
    public partial class ToolBox : UserControl, IToolboxService
    {
        #region Private member fields
        private ListBox _activitiesList;
        private List<Type> _standardActivities = new List<Type>();
        private IServiceProvider _serviceProvider;
        #endregion

        #region Constructors and cleanup methods
        public ToolBox(IServiceProvider provider)
        {
            _serviceProvider = provider;
            Dock = DockStyle.Fill;

            //create a ListBox to view the toolbox items
            _activitiesList = new ListBox();
            _activitiesList.Dock = DockStyle.Fill;
            _activitiesList.ItemHeight = 23;
            _activitiesList.DrawMode = DrawMode.OwnerDrawFixed;
            _activitiesList.DrawItem += new DrawItemEventHandler(ActivitiesList_DrawItem);
            _activitiesList.MouseMove += new MouseEventHandler(ActivitiesList_MouseMove);
            Controls.Add(_activitiesList);

            //create a list of standard activities that we support
            LoadToolboxActivities();

            //add toolbox items for all activities in the list
            CreateToolboxItems(_activitiesList, _standardActivities);
        }
        #endregion

        #region Private implementation methods
        #region Toolbox Item Construction
        private void CreateToolboxItems(ListBox listbox, List<Type> activities)
        {
            listbox.Items.Clear();

            //add Toolbox items for referenced assemblies
            LoadReferencedTypes(listbox);

            //add Toolbox items for standard activities
            foreach (Type activityType in activities)
            {
                ToolboxItem item =
                    CreateItemForActivityType(activityType);
                if (item != null)
                {
                    listbox.Items.Add(item);
                }
            }
        }
        private void LoadReferencedTypes(ListBox listbox)
        {
            ITypeProvider typeProvider =
                _serviceProvider.GetService(typeof(ITypeProvider))
                    as ITypeProvider;
            if (typeProvider == null)
            {
                return;
            }
            foreach (Assembly assembly
                in typeProvider.ReferencedAssemblies)
            {
                Type[] types = assembly.GetTypes();
                foreach (Type type in types)
                {
                    //if the Type is assignable to Activity, then
                    //add it to the toolbox
                    if (typeof(Activity).IsAssignableFrom(type))
                    {
                        ToolboxItem item =
                            CreateItemForActivityType(type);
                        if (item != null)
                        {
                            listbox.Items.Add(item);
                        }
                    }
                }
            }
        }
        private ToolboxItem CreateItemForActivityType(Type activityType)
        {
            ToolboxItem result = null;

            //does the activity type include the ToolboxItemAttribute
            ToolboxItemAttribute toolboxAttribute = null;
            foreach (Attribute attribute in
                activityType.GetCustomAttributes(
                    typeof(ToolboxItemAttribute), true))
            {
                if (attribute is ToolboxItemAttribute)
                {
                    toolboxAttribute = (ToolboxItemAttribute)attribute;
                    break;
                }
            }

            if (toolboxAttribute != null)
            {
                if (toolboxAttribute.ToolboxItemType != null)
                {
                    //construct the ToolboxItemType specified 
                    //by the attribute.
                    ConstructorInfo constructor =
                        toolboxAttribute.ToolboxItemType.GetConstructor(
                            new Type[] { typeof(Type) });
                    if (constructor != null)
                    {
                        result = constructor.Invoke(
                            new Object[] { activityType })
                                as ToolboxItem;
                    }
                }
            }
            else
            {
                //no attribute found
                result = new ToolboxItem(activityType);
            }

            return result;
        }
        private void ActivitiesList_DrawItem(object sender, DrawItemEventArgs e)
        {
            if (e.Index < 0)
            {
                return;
            }

            ActivityToolboxItem item
                = ((ListBox)sender).Items[e.Index] as ActivityToolboxItem;
            if (item != null)
            {
                Graphics graphics = e.Graphics;
                if ((e.State & DrawItemState.Selected)
                    == DrawItemState.Selected)
                {
                    //draw a border around the selected item
                    graphics.FillRectangle(
                        SystemBrushes.Window, e.Bounds);
                    Rectangle rect = e.Bounds;
                    rect.Width -= 2;
                    rect.Height -= 2;
                    graphics.DrawRectangle(SystemPens.ActiveBorder, rect);
                }
                else
                {
                    //not the selected item, just fill the rect
                    graphics.FillRectangle(SystemBrushes.Window, e.Bounds);
                }

                //draw the toolbox item image
                Int32 bitmapWidth = 0;
                if (item.Bitmap != null)
                {
                    graphics.DrawImage(item.Bitmap,
                        e.Bounds.X + 2, e.Bounds.Y + 2,
                        item.Bitmap.Width, item.Bitmap.Height);
                    bitmapWidth = item.Bitmap.Width;
                }

                //add the display name
                graphics.DrawString(item.DisplayName,
                    e.Font, SystemBrushes.ControlText,
                    e.Bounds.X + bitmapWidth + 2, e.Bounds.Y + 2);
            }
        }
        #endregion
        private WorkflowDesignerConfiguration GetConfiguration()
        {
            string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, System.Configuration.ConfigurationManager.AppSettings["configPath"]);
            if (string.IsNullOrEmpty(path))
            {
                throw new FileNotFoundException("Configuration path not set.");
            }

            return GuerillaProgrammer.FileProcessor.ConfigurationManager.ReadWorkflowConfiguration(path);
        }
        private string SearchAssemblyDirectories(string assemblyName, WorkflowDesignerConfiguration configuration)
        {
            string dllName = assemblyName + ".dll";
            foreach (string directory in configuration.Libraries)
            {
                string filePath = Path.Combine(directory, dllName);
                if (true == File.Exists(filePath))
                {
                    return filePath;
                }
            }

            return string.Empty;
        }
        private Type GetTypeFromAssembly(Assembly assembly, string typeName)
        {
            foreach (Type type in assembly.GetTypes())
            {
                if ((typeName == type.Name) && (true == type.IsSubclassOf(typeof(Activity))))
                {
                    return type;
                }
            }

            return null;
        }
        private Type LoadActivityTypeFromAssembly(string activityType, WorkflowDesignerConfiguration configuration)
        {
            try
            {
                string[] namePieces = activityType.Split(new char[] { ',' });

                string assemblyPath = SearchAssemblyDirectories(namePieces[1].Trim(),configuration);
                if (string.IsNullOrEmpty(assemblyPath))
                {
                    return null;
                }
                Assembly activityAssembly = Assembly.LoadFile(assemblyPath);
                if (null == activityAssembly)
                {
                    return null;
                }

                Type actType = GetTypeFromAssembly(activityAssembly, namePieces[0].Trim());
                if (null == actType)
                {
                    return null;
                }

                return actType;
            }
            catch (Exception exc)
            {
                //Logger.LogError("LoadAssembly Error", "An error occurred loading: {0}.\n error:\n{1}", namePieces, exc.Message);
                throw exc;
            }

        }
        private void LoadToolboxActivities()
        {
            WorkflowDesignerConfiguration configuration = GetConfiguration();
            if (null == configuration)
            {
                throw new InvalidOperationException("No configuration available!");
            }
            if (null == configuration.DesignerActivities)
            {
                return;
            }

            if (0 != configuration.DesignerActivities.Count)
            {
                foreach (WorkflowDesignerConfigurationActivity activity in configuration.DesignerActivities)
                {
                    Type activityType = LoadActivityTypeFromAssembly(activity.Type, configuration);
                    if (null != activityType)
                    {
                        _standardActivities.Add(activityType);
                    }
                }
            }
        }
        #endregion

        #region Events handlers
        #region Drag / Drop operation
        private void ActivitiesList_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                if (_activitiesList.SelectedItem is ActivityToolboxItem)
                {
                    ActivityToolboxItem selectedItem =
                        _activitiesList.SelectedItem
                            as ActivityToolboxItem;
                    IDataObject dataObject = SerializeToolboxItem(
                        selectedItem) as IDataObject;
                    DoDragDrop(dataObject, DragDropEffects.All);
                }
            }
        }
        #endregion
        #endregion

        #region Public interface
        #region IToolboxService Members
        public CategoryNameCollection CategoryNames
        {
            get
            {
                return new CategoryNameCollection(
                    new String[] { "WindowsWorkflow" });
            }
        }
        public string SelectedCategory
        {
            get { return "WindowsWorkflow"; }
            set { }
        }
        public ToolboxItem DeserializeToolboxItem(object serializedObject, IDesignerHost host)
        {
            ToolboxItem result = null;
            if (serializedObject is IDataObject)
            {
                result = ((IDataObject)serializedObject).GetData(
                    typeof(ToolboxItem)) as ToolboxItem;
            }
            return result;
        }
        public ToolboxItem DeserializeToolboxItem(object serializedObject)
        {
            return DeserializeToolboxItem(serializedObject, null);
        }
        public object SerializeToolboxItem(ToolboxItem toolboxItem)
        {
            //use a DataObject which is a general data transfer 
            //mechanism used for clipboard and drag-drop operations.
            DataObject dataObject = new DataObject();
            dataObject.SetData(typeof(ToolboxItem), toolboxItem);
            return dataObject;
        }
        public bool SetCursor()
        {
            return false; //just use standard cursor
        }
        public bool IsSupported(object serializedObject,ICollection filterAttributes)
        {
            return true;
        }
        public bool IsSupported(object serializedObject,IDesignerHost host)
        {
            return true;
        }
        public new void Refresh()
        {
            CreateToolboxItems(_activitiesList, _standardActivities);
        }

        #region IToolboxService Members Not Fully Implemented
        public void AddCreator(ToolboxItemCreatorCallback creator,string format, IDesignerHost host)
        {
            throw new NotImplementedException(
                "Method not implemented");
        }
        public void AddCreator(ToolboxItemCreatorCallback creator,string format)
        {
            throw new NotImplementedException(
                "Method not implemented");
        }
        public void AddLinkedToolboxItem(ToolboxItem toolboxItem,string category, IDesignerHost host)
        {
            throw new NotImplementedException(
                "Method not implemented");
        }
        public void AddLinkedToolboxItem(ToolboxItem toolboxItem,IDesignerHost host)
        {
            throw new NotImplementedException(
                "Method not implemented");
        }
        public void AddToolboxItem(ToolboxItem toolboxItem,string category)
        {
            throw new NotImplementedException(
                "Method not implemented");
        }
        public void AddToolboxItem(ToolboxItem toolboxItem)
        {
            throw new NotImplementedException(
                "Method not implemented");
        }
        public ToolboxItem GetSelectedToolboxItem(IDesignerHost host)
        {
            throw new NotImplementedException(
                "Method not implemented");
        }
        public ToolboxItem GetSelectedToolboxItem()
        {
            throw new NotImplementedException(
                "Method not implemented");
        }
        public ToolboxItemCollection GetToolboxItems(string category, IDesignerHost host)
        {
            throw new NotImplementedException(
                "Method not implemented");
        }
        public ToolboxItemCollection GetToolboxItems(string category)
        {
            throw new NotImplementedException(
                "Method not implemented");
        }
        public ToolboxItemCollection GetToolboxItems(IDesignerHost host)
        {
            throw new NotImplementedException(
                "Method not implemented");
        }
        public ToolboxItemCollection GetToolboxItems()
        {
            throw new NotImplementedException(
                "Method not implemented");
        }
        public bool IsToolboxItem(object serializedObject,IDesignerHost host)
        {
            throw new NotImplementedException(
                "Method not implemented");
        }
        public bool IsToolboxItem(object serializedObject)
        {
            throw new NotImplementedException(
                "Method not implemented");
        }
        public void RemoveCreator(string format, IDesignerHost host)
        {
            throw new NotImplementedException(
                "Method not implemented");
        }
        public void RemoveCreator(string format)
        {
            throw new NotImplementedException(
                "Method not implemented");
        }
        public void RemoveToolboxItem(ToolboxItem toolboxItem, string category)
        {
            throw new NotImplementedException(
                "Method not implemented");
        }
        public void RemoveToolboxItem(ToolboxItem toolboxItem)
        {
            throw new NotImplementedException(
                "Method not implemented");
        }
        public void SelectedToolboxItemUsed()
        {
            throw new NotImplementedException(
                "Method not implemented");
        }
        public void SetSelectedToolboxItem(ToolboxItem toolboxItem)
        {
            throw new NotImplementedException(
                "Method not implemented");
        }
        #endregion
        #endregion
        #endregion
    }
}
